#include "Global.h"
#include "Basic/Stream.h"
#include "Regex/MetaLexer.h"
#include "Basic/FileSystem.h"
#include "Shellapi.h"
#include "Windows/GxDebug.h"
#include "Windows/GxWinApp.h"

namespace FinalRendering
{
	namespace Engine
	{
		RefPtr<FR_Global> Global;

		using GxWinGUI::Application;
		using namespace GxMetaParser;

		void CreateGlobal(bool isEditor)
		{
			Global = new FR_Global(isEditor);
		}

		void DestroyGlobal()
		{
			Global = 0;
		}

		class IllegalProfileSyntax
		{};

		FR_Global::FR_Global(bool isEditor)
		{
			String profile;
			String fileName = L"Engine.cfg";
			if (FileExists(fileName))
				profile = ReadTextFile(fileName);
			else
			{
				int i;
				String appPath = Application::GetExePath();
				MakeDirectory(appPath);
				String fileName = appPath + L"Engine.cfg";
				if (FileExists(fileName))
					profile = ReadTextFile(fileName);
				else
					profile = String(L"WorkDir = \"") + appPath + L"\"";
			}
			String lex =	L"Ident = {\\w+}\n"\
				L"Eql = {=}\n"\
				L"Str = {\"([^\\n\"]|\"\")*\"}\n"\
				L"#WhiteSpace = {\\s+}\n";
			MetaLexer lexer(lex);
			LexStream stream;
			lexer.Parse(profile, stream);
			LexStream::LinkNode * node = stream.Begin();
			while (node)
			{
				if (node->Data.TypeID == 0)
				{
					String field = node->Data.Str;
					node = node->GetNext();
					if (node->Data.TypeID != 1)
					{
						throw IllegalProfileSyntax();
					}
					else
					{
						node = node->GetNext();
					}
					String val;
					if (node->Data.TypeID != 2)
					{
						throw IllegalProfileSyntax();
					}
					else
					{
						val = node->Data.Str;
						node = node->GetNext();
					}
					val = val.Mid(1, val.GetLength()-2);
					if (field == L"WorkDir")
					{
						workDir = val;
					}
				}
				else
					node = node->GetNext();
			}
			LoadEntityPlugins(isEditor);
		}

		FR_Global::~FR_Global()
		{
			UnloadEntityPluginModules();
		}

		String FR_Global::GetWorkingDirectory()
		{
			return workDir;
		}

		void FR_Global::UnloadEntityPluginModules()
		{
			for (int i=0; i<modules.Count(); i++)
			{
				if (modules[i].destroy)
					modules[i].destroy();
				FreeLibrary(modules[i].module);
			}
			modules.Clear();
		}

		void FR_Global::LoadEntityPlugins(bool isEditor)
		{
			pluginLoadErr = false;
			pluginLoadErrInfo = L"";

			String pluginDir = Application::GetExePath() + L"./Plugins/";
			
			entityDescRegistry.Clear();
			UnloadEntityPluginModules();

			for (gxFileIterator iter(pluginDir, "*.dll"); !iter.IsEnd(); iter.MoveNext())
			{
				String fileName = iter.GetCurrentFileName();
				HMODULE module = LoadLibrary((pluginDir + fileName).GetStrPtr());
				try
				{
					if (module)
					{
						InitProc init = (InitProc)GetProcAddress(module, "EntityPlugin_Init");
						if (init)
							init(isEditor, 0, module);
						GetClassesCountProc GetClassesCount = (GetClassesCountProc)GetProcAddress(module, "EntityPlugin_GetClassesCount");
						if (!GetClassesCount)
							throw 0;
						int count = GetClassesCount();
						for (int i=0; i<count; i++)
						{
							GetEntityDescProc GetEntityDesc = (GetEntityDescProc)GetProcAddress(module, "EntityPlugin_GetEntityDesc");
							if (!GetEntityDesc)
								throw 0;
							IEntityDesc * desc = GetEntityDesc(i);
							if (desc)
							{
								entityDescRegistry.Add(desc->ClassName(), desc);
							}
							else
								throw 0;
						}
					}
					else
					{
						throw 0;
					}
				}
				catch(int)
				{
					pluginLoadErrInfo += L"Error loading \"";
					pluginLoadErrInfo += fileName;
					pluginLoadErrInfo += L"\"\n";
					pluginLoadErr = true;
				}
				PluginModule m;
				m.destroy = (DestroyProc)GetProcAddress(module, "EntityPlugin_Destroy");
				m.module = module;
				modules.Add(m);
			}

			GxWinSystem::Debug::WriteLine(pluginLoadErrInfo);
		}

		String FR_Global::GetPluginLoadErrorInfo()
		{
			return pluginLoadErrInfo;
		}

		bool FR_Global::IsPluginLoadError()
		{
			return pluginLoadErr;
		}

		IEntityDesc * FR_Global::GetEntityDesc(const String & className)
		{
			IEntityDesc * desc = 0;
			entityDescRegistry.Lookup(className, desc);
			return desc;
		}

		int FR_Global::GetEntityPluginCount()
		{
			return entityDescRegistry.Count();
		}

		IEntityDesc * FR_Global::GetEntityDesc(int i)
		{
			return entityDescRegistry[i].Value;
		}
	}
}